Como esta sua situacao financeira? Caso tenha alguma reserva pode ser interessante pensar em investimentos pois a poupança já não é mais garantia de lucro no longo prazo, não acredita?
Já estamos no início de 2020 e desde 2019 já se liam notícias como esta abaixo que levam à reflexão sobre reeducação financeira pois alertam a sobre a necessidade de procurar novas oportunidades de investimento.
Com a finalidade de fomentar um pouco a discussão sobre investimentos, trouxe nesse post algumas sugestões e idéias sobre como otimizar nossas escolhas e tentar equilibrar nosso risco em novos investimentos combinando elementos de estatística, machine learning e programação em R.
AVISO: Este post não tem como finalidade ser um guia de investimentos (Já existem muitas consultorias especializadas nisso por ai). Todos as decisões tomadas como diversificação da carteira, seleção de ações e critérios para desmontagem da carteira são exemplos e servem para ilustrar algumas possibilidades que um cientista de dados têm na hora de desenvolver ferramentas para auxiliar à tomada de decisão.
Ao final do post criaremos um robo que analisará as cotações e enviará menssagens para nós com uma tabela financeira automatizada via telegram como mostra na animação:
Esse será nosso objetivo final, então mão à obra!
De acordo com um especialista entrevistado pela InfoMoney:
“Poupar é guardar dinheiro para usar no futuro, comprar alguma coisa com ele. Investimento é juntar dinheiro, não mexer nele, para que este gere rendimentos e aí sim, usar os lucros mais para frente. É o recomendado para quem quer viver de renda no futuro, por exemplo” (…)InfoMoney - Ago 2015
Ou seja, poupar é acumular agora para utilizr depois, e normalmente envolve mudança de hábitos, pois requer uma redução nos gastos pessoais e familiares, já Investir é usar esse dinheiro poupado em aplicações que rendam.
Como todo mundo sabe, não existe investimento sem risco e este risco deve ser controlado e utilizado a nosso favor de forma que gere alguma segurança financeira.
Provamente você já ouviu essa frase alguma vez na vida e ela certamente faz sentido. Não colocar todos os ovos na mesma cesta significa que você deve diversificar o seu investimento.
Diversificar a carteira irá proteger seus investimentos diminuindo o risco pois, imagine só, você investe todo o seu dinheiro em uma empresa e ela passa por alguma crise assim seu dinheiro estará todo comprometido!
Existem diversos motivos para se diversificar a carteira mas acho que essa metáfora dos ovos já resume bem.
Dependendo do risco que você deseja se expor existem muitas formas de preparar a carteira mas a idéias principal é a seguinte:
Para ajudar a dividir a carteira de investimentos neste post utilizaremos a chamada Regra (ou Lei) dos 80.
A estratégia é a seguinte: subtraia da sua idade o número 80. O resultado dessa conta vai indicar o percentual a ser investido em renda variável. Por exemplo, no meu caso: tenho 26 anos, portanto \(80-26 = 54\%\) deverá ser investido em renda variável. Aos 53 anos esse percentual vai cairá para a metade, \(27\%\).
A idéias principal por trás desta regra que é que a cada ano que passa, 1% do montante da renda variável deva ser direcionado para a renda fixa.
Para testar diferentes valores seguindo esta regra desenvolvi uma função que se chama montagem80() (que já está disponível no github TODO). Vejamos alguns resultaodos para diferentes cenários e vamos selecionar um para seguir com a montagem da carteira:
source("montagem80.R")
1. Entrada de R$20.000,00
montagem80(entrada = 20000, idade = 26)
## • Entrada: R$R$20.000,00
## └─ Renda fixa: R$9.200,00
## └─ Renda variavel: R$10.800,00
## (Acao + Cryptomoeda):
## R$9.720,00 + R$1.080,00
montagem80(variavel = 5000, idade = 53)
## • Entrada: R$R$18.518,52
## └─ Renda fixa: R$13.518,52
## └─ Renda variavel: R$5.000,00
## (Acao + Cryptomoeda):
## R$4.500,00 + R$500,00
Utilizaremos a primeira (1.) que esta assinalada em vermelho como exemplo, onde:
Após definir a quantidade a ser investida é hora de planejar a próxima estapa da divessificação.
Normalmente diversificamos nossa renda fixa também mas como gostaria de focar nas análises de renda variável utilizaremos o simulador disponivel no site https://verios.com.br/ neste link para avaliar nossa escolha:
A título de exemplo, escolhi o Tesouro Prefixado 2015 (LTN), note que com essa escolha o lucro planejado seria de quase 3 mil reais nos próximos 5 anos.
Volto a lembrar que esta seleção é apenas um exemplo e existem diversas informações a serem levadas em conta ao se fazer esta escolha. Convido o leitor a procurar saber mais sobre os tipos, prós e contras do Tesouro Direto.
Para compor ajudar a compor a parte da cateira que de renda variável selecionei duas ações (de forma totalmente arbitrária para exemplificar neste post) que são inversamente correlacionadas baseado no excelente post "Estudo de correlação entre ações da Bolsa de Valores de São Paulo" escrito por Victor Gomes onde o autor faz um estudo de correlações das séries históricas de ações de diferentes setores.
O Bitcoin foi selecionado para completar a carteira de renda variável como um ativo de alto risco com bastante volatividade, mas por que assumir este risco?
São muitas as histórias de pessoas que ficaram milhorárias com o bitcoin pela sua valorização inesperada ao longo do tempo, como por exemplo o adolescente que ficou milionário aos 18 anos usando bitcoins após fazer aposta com os pais.
Então eu acho que 10% dessa nossa carteira (não esqueça que podemos ter mais de uma carteira) é um risco que pode valer a pena correr e por isso vou incluí-lo.
Portanto, para este exemplo consideramos:
# portifolio = c("PETR4.SA","FIBR3.SA", "BTC-USD")
portifolio = c("TUPY3.SA","ELET3.SA", "BTC-USD")
Faremos a aquisição das série históricas das cotações destes ativoes deste 01/01/2016 utilizando o pacote tidyquant que nos retorna os dados das cotações das ações informadas em formado “arrumado” (familiar com funções do tidyverse), veja:
library(tidyquant) # aquisicao de dados financeiros
# stocks <-map_df(portifolio, ~tq_get(.x, get = "stock.prices", from = " 2016-01-01"))
# saveRDS(stocks, "stocks.rds")
stocks <- readRDS("stocks.rds")
tsibble Para facilitar na manipulação dos dados obtidos vamos converte-los para o formato tsibble que fornece uma porção de funcionalidades para a análise de séries temporais permitindo seguirmos o seguinte fluxo de trabalho com mais fluidez:
library(tsibble) # series temporais arrumadas
tbl_stocks <- stocks %>% as_tsibble(key = symbol, index = date)
Após converter para tsibble, vamos preencher espaçoes com dados faltantes (como finais de semana, que a bolsa não é operada) como valor do dia anterior:
tbl_stocks <-
tbl_stocks %>%
fill_gaps() %>%
tidyr::fill(c(open, high, low, close, volume, adjusted),.direction = "down")
Vejamos qual foi o comportamento das séries históricas desde o início de 2016
library(forecast) # series temporais
library(fpp3) # series temporais
d2 <-
tbl_stocks %>%
group_by(symbol) %>%
summarise(y = mean(close))
autoplot(tbl_stocks)+
facet_wrap(~symbol, scales = "free_y", ncol = 1)+
theme(legend.position = "bottom") +
geom_area(aes(color = symbol, fill = symbol),
alpha = 0.5, position = position_dodge(0.8)) +
stat_smooth(color = "#FC4E07", fill = "#FC4E07", linetype = 2, size = 0.7,method = "loess")
A série do Bitcoin é a mais imprevisível, houve um pico em 2018 mas após isso não houve nenhum grande pico como aquele. Em breve ocorrerá o Halving em breve (a contagem regressiva pode acompanhada neste link) que é um processo de choque de oferta e ocorre aproximadamente a cada 4 anos e pode ser uma boa oportunidade de retorno.
As duas séries da bolsa de valores não parecem ter uma corerlação muito forte, os picos ocorrem de forma alternada e isto pode ser uma caracteristica boa para a carteira pois caso uma delas entre em crise a outra poderá estar em uma fase boa.
Para confirmar as conjecturas formdas ao observar o comportamento das séries histórias vamos dar uma olhada nos gráficos de dispersão e coeficientes de correlação de Spearman
tbl_stocks %>%
as_tibble() %>%
select(symbol, date, close) %>%
spread(key = symbol, value = close) %>%
GGally::ggpairs(columns = 2:4,
upper = list(continuous = GGally::wrap("cor", method = "spearman")),
lower = list(continuous = GGally::wrap("points", alpha = 0.3,size=0.5)))
https://www.business-science.io/code-tools/2017/10/28/demo_week_h2o.html
data_split1 <- Sys.Date()-30*3
data_split2 <- Sys.Date()-30*1
pp_data <- function(x){
tryCatch({
x %>%
transmute(close,
day = lubridate::day(date),
month = lubridate::month(date),
year = lubridate::year(date),
wday = lubridate::wday(date),
date)
}, error = function(e){
x %>%
transmute(day = lubridate::day(date),
month = lubridate::month(date),
year = lubridate::year(date),
wday = lubridate::wday(date))
})
}
tbl_future1 <- tibble(date = seq.Date(Sys.Date(), Sys.Date()+30, "day"))
tbl_future <- pp_data(tbl_future1)
library(h2o) # automl
# h2o.init()
tbl_stocks_automl <-
tbl_stocks %>%
as_tibble() %>%
group_by(symbol) %>%
nest() %>%
mutate(data = map(data, ~pp_data(.x) )) %>%
bind_cols(tibble(future_h2o = list(as.h2o(tbl_future),
as.h2o(tbl_future),
as.h2o(tbl_future))) ) %>%
mutate(train_h2o = map(data, ~.x %>%
filter(date <= data_split1) %>%
select(-date) %>% as.h2o())) %>%
mutate(valid_h2o = map(data, ~.x %>%
filter(date > data_split1 & date <= data_split2) %>%
select(-date) %>% as.h2o()) ) %>%
mutate(test_h2o = map(data, ~.x %>%
filter(date > data_split2) %>%
select(-date) %>% as.h2o())) %>%
mutate(automl_models_h2o = pmap(list(train_h2o = train_h2o,
valid_h2o = valid_h2o,
test_h2o = test_h2o),
function(train_h2o, valid_h2o, test_h2o){
automl_models_h2o <- h2o.automl(
x = setdiff(names(train_h2o), "close"),
y = "close",
training_frame = train_h2o,
validation_frame = valid_h2o,
leaderboard_frame = test_h2o,
max_runtime_secs = 60*5,
verbosity = NULL,
stopping_metric = "deviance")
})) %>%
mutate(automl_leader = map(automl_models_h2o, ~ .x@leader)) %>%
mutate(pred_h2o = map2(automl_leader, test_h2o, ~h2o.predict(.x, newdata = .y))) %>%
mutate(pred_future_h2o = map2(automl_leader, future_h2o, ~h2o.predict(.x, newdata = .y))%>%
map(~as_tibble(.x) %>% bind_cols(tbl_future1))) %>%
mutate(error_tbl = map2(data, pred_h2o,
~.x %>% filter(date > data_split2) %>%
add_column(pred = .y %>% as_tibble() %>% pull(predict)) %>%
rename(actual = close) %>%
mutate(error = actual - pred,
error_pct = error / actual) ))
saveRDS(tbl_stocks_automl, "tbl_stocks_automl.rds")
tbl_stocks_automl <- readRDS("tbl_stocks_automl.rds")
Avaliar performance dos modelos:
library(patchwork) # organizar plots
# map2(tbl_stocks_automl$automl_leader, tbl_stocks_automl$test_h2o,
# ~ h2o.performance(.x, newdata = .y))
pmap(list(.x = tbl_stocks_automl$data,
.y = tbl_stocks_automl$error_tbl,
.z = tbl_stocks_automl$symbol,
.w = tbl_stocks_automl$pred_future_h2o),
function(.x, .y, .z, .w){
.x %>%
ggplot(aes(x = date, y = close)) +
geom_rect(xmin = as.numeric(data_split1),
xmax = as.numeric(data_split2),
ymin = 0, ymax = Inf, alpha = 0.015,
fill = "#C1C3E3") +
geom_rect(xmin = as.numeric(data_split2),
xmax = as.numeric(Sys.Date()),
ymin = 0, ymax = Inf, alpha = 0.015,
fill = "#E8D3D3") +
geom_line(aes(y = close), size = 0.5) +
geom_line(aes(y = predict), size = 0.5, data = .w, color = "green") +
geom_ma(n = 30, color = "red") +
geom_line(aes(y = pred), color = "blue", size = 0.5, data = .y)+
labs(y = .z)
}
) %>% { .[[1]] / .[[2]] / .[[3]] }
Parece interessante, vamos às compras!
após todo o estudo para a elaboracao da carteira chegou a hora das compras.
Suponha que tivessemos realizado nossas compra no fechamento do dia 09/01/2020, quando as cotações eram as seguintes:
tbl_stocks %>%
filter(date == "2020-01-09") %>%
mutate(close = cell_spec(moeda_real(close), "html", color = "blue")) %>%
mutate_at(c(3:5, 8), ~moeda_real(.x)) %>%
kable2()
| symbol | date | open | high | low | close | volume | adjusted |
|---|---|---|---|---|---|---|---|
| BTC-USD | 2020-01-09 | R$8.082,30 | R$8.082,30 | R$7.842,40 | R$7.879,07 | 24045990465 | R$7.879,07 |
| ELET3.SA | 2020-01-09 | R$ 39,31 | R$ 39,72 | R$ 39,08 | R$ 39,35 | 4357400 | R$ 39,35 |
| TUPY3.SA | 2020-01-09 | R$ 25,84 | R$ 26,05 | R$ 25,60 | R$ 26,05 | 465400 | R$ 26,05 |
cot_inicio = c(elet = 39.35, tupy = 26.05)
qtd_inicio = c(elet = 190, tupy = 90)
Neste dia a cotação para ELET3 era R$39,35 e TUPY3 era R$26,05 e suponha que tenhamos comprado 190 lotes fracionarios de ELET3 e 90 de TUPY, totalizando R$9.821,00 (próximo ao que avíamos planejado no inicio do estudo)
Note que o valor do Bitcoin está em dólares, para obter o valor em reai daquele dia vamos utilizar a API do Mercado Bitcoin:
library(jsonlite) # requisicao de api
url <- glue::glue("https://www.mercadobitcoin.net/api/BTC/day-summary/2020/01/09/")
safe_fromJSON <- safely(fromJSON, as.numeric(NA))
consulta <- safe_fromJSON(url)$result %>% as_tibble()
consulta %>%
select(-date) %>%
mutate_all(~moeda_real(.x)) %>%
mutate(closing = cell_spec(closing, "html", color = "blue")) %>%
kable2()
| opening | closing | lowest | highest | volume | quantity | amount | avg_price |
|---|---|---|---|---|---|---|---|
| R$32.580 | R$32.243,90 | R$32.000 | R$32.780,40 | R$5.636.666,00 | R$174,37 | R$4.582 | R$32.326,16 |
O preço de fechamento foi de R$32.243,90, suponhamos que tenha sido este o valor pago no dia. (Parece que neste dia o dolar estava em torno de R$4,09)
cot_inicio[3] <- consulta$closing
qtd_inicio[3] <- 0.032
Portanto, ao valor de R$32.243,90 compramos 0.032 Bitcoin totalizano 1031.8048 completando nossa carteira.
Semelhante a uma planilha financeira, criaremos uma tabela financeira automatizada que receberá como input os valores da montagem e calculará automaticamente os valores do desmontagem no tempo atual utilizando dados de apis abertas.
Primeiro valos obter as cotacoes mais recentes das cotações das acoes que compramos na bolsa:
Obter dados da alphavantager https://www.business-science.io/code-tools/2017/09/03/alphavantager-0-1-0.html
library(alphavantager) # api para streaming
av_api_key(key)
consulta_acoes <- map_df(portifolio[1:2], ~{
av_get(symbol = .x,
av_fun = "TIME_SERIES_INTRADAY",
interval = "1min", # "1min", "5min", "15min", "30min" ou "60min"
outputsize = "compact") %>% # "full"
bind_cols(stock = rep(.x, nrow(.)))
}
)
consulta_acoes %>%
as_tsibble(key = stock, index = timestamp) %>%
autoplot()+
facet_wrap(~stock, scales = "free_y", ncol = 1)+
theme(legend.position = "bottom")
Coletar a cotação do Bitcoin (em reais) mais recente:
coin <- "BTC"
method <- "ticker"
url <- glue::glue("https://www.mercadobitcoin.net/api/{coin}/{method}/")
safe_fromJSON <- safely(fromJSON, as.numeric(NA))
consulta_bitcoin <-
safe_fromJSON(url)$result$ticker %>%
as_tibble() %>%
transmute(timestamp = lubridate::ymd_hms(as.POSIXct(date, origin="1970-01-01")),
open, high, low, close = sell, volume = NA, stock = "BTC.BR") %>%
mutate_at(c('open', 'high', 'low', 'close'), ~as.numeric(.x))
Combinar e padronizar as requisições:
consulta_atual <-
bind_rows(
consulta_acoes %>%
group_by(stock) %>%
filter(timestamp == last(timestamp))
,
consulta_bitcoin
)
consulta_atual %>%
mutate_at(2:4, ~moeda_real(.x)) %>%
mutate(close = cell_spec(moeda_real(close), "html", color = "blue")) %>%
kable2()
| timestamp | open | high | low | close | volume | stock |
|---|---|---|---|---|---|---|
| 2020-03-13 15:54:00 | R$15,63 | R$15,63 | R$15,63 | R$15,63 | 85 | TUPY3.SA |
| 2020-03-13 15:54:00 | R$27,76 | R$27,90 | R$27,65 | R$27,90 | 3910 | ELET3.SA |
| 2020-03-16 02:02:20 | R$27.596,68 | R$30.000,00 | R$27.040,14 | R$27.988,96 | NA | BTC.BR |
# Tabela resultado
financas <-
tibble(
ativo = portifolio,
# inicio
cot_inicio = cot_inicio,
qtd_inicio = qtd_inicio,
vol_inicio = cot_inicio * qtd_inicio,
# Desinicio / Atual
cot_atual = consulta_atual$close,
qtd_atual = qtd_inicio,
vol_atual = cot_atual * qtd_atual,
# Resultado
ganho_perda = vol_atual - vol_inicio,
resultado_bruto = ganho_perda / vol_inicio * 100
)
porcentagem <- function(x){paste0(round(x,2), "%")}
# Exibicao
financas %>%
mutate(
cot_inicio = moeda_real(cot_inicio),
cot_atual = moeda_real(cot_atual),
vol_inicio = moeda_real(vol_inicio),
vol_atual = moeda_real(vol_atual),
qtd_inicio = round(qtd_inicio,4),
qtd_atual = round(qtd_atual,4),
# vol_atual = color_bar("lightgreen")(round(vol_atual, 2)),
# vol_inicio = color_bar("lightgreen")(round(vol_inicio, 2)),
`Vender?` = ifelse(ganho_perda > 0,"\u2713", "\u2718") ,
ganho_perda = cell_spec(moeda_real(ganho_perda), "html",
color = ifelse(ganho_perda > 0, "green", "red")),
resultado_bruto = cell_spec(porcentagem(resultado_bruto), "html",
color = ifelse(resultado_bruto > 0, "green", "red"))) %>%
kable(format = "html", escape = F) %>%
kable_styling(c("striped", "bordered", "hover", "responsive"), full_width = F) %>%
add_header_above(c(" ", "Montagem" = 3, "Desmontagem / Atual" = 3, "Resultado" = 3))
| ativo | cot_inicio | qtd_inicio | vol_inicio | cot_atual | qtd_atual | vol_atual | ganho_perda | resultado_bruto | Vender? |
|---|---|---|---|---|---|---|---|---|---|
| TUPY3.SA | R$ 39,35 | 190.000 | R$7.476,50 | R$ 15,63 | 190.000 | R$2.969,70 | R$-4.506,80 | -60.28% | ✘ |
| ELET3.SA | R$ 26,05 | 90.000 | R$2.344,50 | R$ 27,90 | 90.000 | R$2.511,00 | R$ 166,50 | 7.1% | ✓ |
| BTC-USD | R$32.243,90 | 0.032 | R$1.031,80 | R$27.988,96 | 0.032 | R$ 895,65 | R$ -136,16 | -13.2% | ✘ |
library(telegram) # conectar bot telegram
# Definir bot
bot <- TGBot$new(token = bot_token('fgstockbot'))
# Conectar ao R
bot$set_default_chat_id(bot$getUpdates()$message$chat$id[1])
report_stocks <- function(frequencia = 60) {
# load("historico.RData")
# loop infinito
while(
Sys.time() > lubridate::ymd_hm("2020-03-14 10:00") & # abertura
Sys.time() < lubridate::ymd_hm(paste0(Sys.Date(), " 17:00")) # fechamento
) {
# Requisicao das cotacoes atuais
# Acoes
consulta_acoes <- map_df(portifolio[1:2], ~{
cat(paste0("Coletar: ", .x, "...\n"))
av_get(symbol = .x,
av_fun = "TIME_SERIES_INTRADAY",
interval = "1min", # "1min", "5min", "15min", "30min" ou "60min"
outputsize = "compact") %>% # "full"
bind_cols(stock = rep(.x, nrow(.)))
}
)
cat(paste0("Coletar: BTC.BR...\n"))
# Bitcoin
coin = "BTC"
method = "ticker"
url = glue::glue("https://www.mercadobitcoin.net/api/{coin}/{method}/")
safe_fromJSON <- safely(fromJSON, as.numeric(NA))
consulta_bitcoin <-
safe_fromJSON(url)$result$ticker %>%
as_tibble() %>%
transmute(timestamp = lubridate::ymd_hms(as.POSIXct(date, origin="1970-01-01")),
open, high, low, close = sell, volume = NA, stock = "BTC.BR") %>%
mutate_at(c('open', 'high', 'low', 'close'), ~as.numeric(.x))
cat(paste0("Combinar bases...\n"))
# Combinar requisicoes
consulta_atual <-
bind_rows(
consulta_acoes %>%
group_by(stock) %>%
filter(timestamp == last(timestamp))
,
consulta_bitcoin
)
# verifica se a API retornou uma lista
if(!is.null(consulta_atual)) {
cat(paste0("Calcular retornos...\n"))
# Tabela resultado
financas <-
tibble(
ativo = portifolio,
# inicio
cot_inicio = cot_inicio,
qtd_inicio = qtd_inicio,
vol_inicio = cot_inicio * qtd_inicio,
# Desinicio / Atual
cot_atual = consulta_atual$close,
qtd_atual = qtd_inicio,
vol_atual = cot_atual * qtd_atual,
# Resultado
ganho_perda = vol_atual - vol_inicio,
resultado_bruto = ganho_perda / vol_inicio * 100
)
porcentagem <- function(x){paste0(round(x,2), "%")}
# Exibicao
tabela <-
financas %>%
mutate(
cot_inicio = moeda_real(cot_inicio),
cot_atual = moeda_real(cot_atual),
vol_inicio = moeda_real(vol_inicio),
vol_atual = moeda_real(vol_atual),
qtd_inicio = round(qtd_inicio,4),
qtd_atual = round(qtd_atual,4),
# vol_atual = color_bar("lightgreen")(round(vol_atual, 2)),
# vol_inicio = color_bar("lightgreen")(round(vol_inicio, 2)),
`Vender?` = ifelse(ganho_perda > 0,"\u2713", "\u2718") ,
ganho_perda = cell_spec(moeda_real(ganho_perda), "html",
color = ifelse(ganho_perda > 0, "green", "red")),
resultado_bruto = cell_spec(porcentagem(resultado_bruto), "html",
color = ifelse(resultado_bruto > 0, "green", "red"))) %>%
kable(format = "html", escape = F) %>%
kable_styling(c("striped", "bordered", "hover", "responsive"), full_width = F) %>%
add_header_above(c(" ", "Montagem" = 3, "Desmontagem / Atual" = 3, "Resultado" = 3))
save_kable(tabela, file = "table.png", self_contained = F)
cat("Enviando menssagem..\n")
bot$sendPhoto('table.png', caption = paste0("Tabela de resultados - ",Sys.time()))
cat("Menssagem enviada!\n")
}
cat(paste0("Aguarde ", frequencia, " segundos..\n"))
Sys.sleep(frequencia/2)
cat(paste0("Aguarde ", round(frequencia/2,1), " segundos..\n"))
Sys.sleep(frequencia/2)
}
}